# gozerlib/commands.py
#
#

""" commands. """

## gozerlib imports

from gozerlib.utils.xmpp import stripped
from gozerlib.utils.trace import calledfrom
from gozerlib.utils.exception import handle_exception
from gozerlib.users import users
from gozerlib.config import cfg

## basic imports

import logging
import sys

class Command(object):

    """ 
        a command object. 

        :param modname: the module name in which this command is registered
        :type modname: string
        :param cmnd: the command
        :type cmnd: string
        :param func: function handling the command
        :type func: function or method
        :param perms: permission needed to be allowed to do this command
        :type perms: list of strings
        :param options: options used in this command
        :type options: dict
        :param allowqueue: whether this command can have queues attached
        :type allowqueue: boolean

    """

    def __init__(self, modname, cmnd, func, perms=[], options={}, allowqueue=True):
        self.modname = modname
        self.plugname = self.modname.split('.')[-1]
        self.cmnd = cmnd
        self.func = func
        self.perms = perms
        self.plugin = self.plugname
        self.options = options
        self.allowqueue = allowqueue

class Commands(object):

    """ the commands object holds all commands of the bot. """

    def __init__(self):
        self.store = {}

    def add(self, cmnd, func, perms=[], threaded=False, options={}, allowqueue=False):

        """
            add a command. 

            :param cmnd: the command
            :type cmnd: string
            :param func: function handling the command
            :type func: function or method
            :param perms: permission needed to be allowed to do this command
            :type perms: list of strings
            :param threaded: whether this command need to be executed in its own thread
            :type plugin: boolean
            :param options: options used in this command
            :type options: dict
            :param allowqueue: whether this command can have queues attached
            :type allowqueue: boolean
            :rtype: self

        """

        modname = calledfrom(sys._getframe())
        self.store[cmnd] = Command(modname, cmnd, func, perms, options, allowqueue)
        return self

    def dispatch(self, bot, event):

        """
            dispatch an event if cmnd exists and user is allowed to exec this command.

            :param bot: the bot on which event originated
            :type bot: gozerlib.botbase.BotBase
            :param event: event to dispatch
            :type event: gozerlib.eventbase.EventBase
            :rtype: None

        """

        from gozerlib.aliases import aliascheck
        aliascheck(event)


        try:
            cmnd = event.txt.split()[0]
        except IndexError:
            logging.debug("commands - %s - not txt provided" % event.userhost)
            return

        if self.store.has_key(cmnd):

            ## core business

            try:
                c = self.store[cmnd]
                id = event.auth or stripped(event.userhost)

                if users.allowed(id, c.perms):
                    logging.warn('dispatching %s for %s' % (cmnd, id))
                    c.func(bot, event)

                elif cfg['auto_register']:

                    if not 'OPER' in c.perms and not users.exists(id, event.type):

                        if users.add(id, [id, ], ['USER', ], event.type):

                            if 'USER' in c.perms:
                                logging.warn('dispatching %s for %s' % (cmnd, id))
                                c.func(bot, event)

                    else:
                        logging.warn('%s is not allowed to do the %s command' % (id, cmnd))

                        if cfg['auto_register']:
                            event.reply("you are not allowed to do the %s command .. need perms: %s" % (cmnd, c.perms))
                else:
                    logging.warn('%s is not allowed to do the %s command' % (id, cmnd))

                    if cfg['auto_register']:
                        event.reply("you are not allowed to do the %s command .. need perms: %s" % (cmnd, c.perms))

            except Exception, ex:
                handle_exception(event)

            if event.queues:
                for queue in event.queues:
                    queue.put_nowait(None)

            return True

        return False


    def unload(self, modname):

        """
            remove modname registered commands from store.

            :param modname: name of the module to unload
            :type modname: string
            :rtype: self

        """

        delete = []

        for name, cmnd in self.store.iteritems():
            if cmnd.modname == modname:
                delete.append(cmnd)

        for cmnd in delete:
            del cmnd

        return self

    def exists(self, cmnd):

        """
            see if a command exists.

            :param cmnd: the command to test if it exists
            :type cmnd: string
            :rtype: boolean

        """

        if self.store.has_key(cmnd):
            return True

        return False

    def apropos(self, search):

        """
            search existing commands for search term. 

            :param search: text to search for
            :type search: string
            :rtype: list of plugin names
 
        """
 
        result = []

        for name, cmnd in self.store.iteritems():

            if search in name:
                result.append(name)

        return result

    def perms(self, cmnd):

        """
            show what permissions are needed to execute cmnd

            :param cmnd: command to get parameters for
            :type cmnd: string
            :rtype: list of permissions (strings) or None

        """

        try:
            return self.store[cmnd].perms
        except (KeyError, ValueError):
            return

    def list(self):

        """
            return list of all plugins. 

            :rtype: list of plugins (strings)

        """

        return self.store.keys()

    def whereis(self, cmnd):

        """ 
            return plugin name in which command is implemented. 

            :param cmnd: command to get plugin for
            :type cmnd: string
            :rtype: string (plugin name)

        """

        try:
            return self.store[cmnd].plugname
        except KeyError:
            return

    def gethelp(self, cmnd):

        """
            get the docstring of a command. used for help.

            :param cmnd: command to get docstring of
            :type cmnd: string
            :rtype: string

        """

        try:
            return self.store[cmnd].func.__doc__
        except (KeyError, AttributeError):
            return

# the command object
cmnds = Commands()
